home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacWorld 1997 January
/
Macworld (1997-01).dmg
/
Games World
/
Shareware Games
/
Arcade
/
Mike's Breakout
/
Graphics.u
< prev
next >
Wrap
Text File
|
1996-10-11
|
14KB
|
480 lines
{send all comments/gripes/etc to ani@atlas.nmsu.edu}
{feel free to use this code for whatever you'd like. i'd be grateful i helped make someone's day just a bit brighter}
unit Graphics;
interface
uses
TypeConst, MTGW;
var {explained when they are used}
NonMovingList: array[1..600] of Point;
NonMovingGrid: array[0..MAXGRIDX] of array[0..MAXGRIDY] of array[1..5] of ListGameObject;
CIcons: array[MINCICON..MAXCICON] of CIconHandle;
OffScreenWorld: GrafPtr;
TempOff: GWorldPtr;
NextDrawing: longint;
CurrentNonMoving, DeathCountdown, NumberRects: integer;
CopyRects: array[1..600] of Rect;
GridRect, VFlameRect, FlameRect, AllocatedRect: Rect;
BackPat: PixPatHandle;
StatusPat: PixPatHandle;
Window: WindowPtr;
StatusStart: Point;
TempBool: Boolean;
GWorldStart, ScreenStart: Ptr;
lives, Missiles, top, Score, GOffset, SOffset: longint;
procedure XCopyBits (Where: Rect);
procedure DrawCopyRects;
function BiggestRect (First, Second: Rect): Rect;
procedure FlushLevelGraphics (WhatPixPat: integer);
procedure InitGraphics;
procedure Draw (var WhatOb: ListGameObject);
procedure DrawPaddle (var WhatOb: ListGameObject);
procedure DrawBrick (var WhatOb: ListGameObject);
procedure DrawBall (var WhatOb: ListGameObject);
procedure DrawMissile (var WhatOb: ListGameObject);
procedure DrawPaddlePiece (var WhatOb: ListGameObject);
procedure DrawPaddleExp (var WhatOb: ListGameObject);
procedure KABLAMMM (var WhatOb: ListGameObject);
procedure DrawDeadBrick (var WhatOb: ListGameObject);
procedure AddCopyRect (Where: Rect);
procedure FixErasure (FirstObject: ListGameObject);
procedure RestoreGraphics;
procedure CoordAndSize (var WhichRect: Rect; RLeft: integer; RTop: integer; RWidth: integer; RHeight: integer);
procedure DrawGridSquares;
procedure DrawGridSingleSquare (var Where: Point);
procedure DrawStatusBar;
procedure UpdateStatusBar;
implementation
procedure CoordAndSize;
begin
with WhichRect do
begin
top := RTop;
left := RLeft;
bottom := RTop + RHeight;
right := RLeft + RWidth;
end;
end; {this is just a macro to set a rect quickly}
procedure AddCopyRect (Where: Rect);
begin
NumberRects := NumberRects + 1;
if NumberRects < 11 then
CopyRects[NumberRects] := Where;
end; {add a new rect to be copied onto the screen}
function BiggestRect;
begin
if Second.top < First.top then
AllocatedRect.top := Second.top
else
AllocatedRect.top := First.top;
if Second.left < First.left then
AllocatedRect.left := Second.left
else
AllocatedRect.left := First.left;
if Second.right > First.right then
AllocatedRect.right := Second.right
else
AllocatedRect.right := First.right;
if Second.bottom > First.bottom then
AllocatedRect.bottom := Second.bottom
else
AllocatedRect.bottom := First.bottom;
BiggestRect := AllocatedRect;
end; {quite simply, find the smallest rect that can fit two larger ones}
procedure DrawBrick;
begin
with WhatOb^ do
begin
AddCopyRect(CurrentPlace);
if DoDispose then
EraseRect(CurrentPlace)
else
PlotCIcon(CurrentPlace, CIcons[frame]);
end;
end;
procedure DrawBall;
begin
with WhatOb^ do
begin
frame := frame + 1;
if frame = 308 then
frame := 300;
PlotCIcon(CurrentPlace, CIcons[frame]);
AddCopyRect(BiggestRect(LastPlace, CurrentPlace));
LastPlace := CurrentPlace;
end;
end; {draw the ball, go to the next frame}
procedure DrawMissile;
begin
with WhatOb^ do
begin
PlotCIcon(CurrentPlace, CIcons[600]);
AddCopyRect(BiggestRect(LastPlace, CurrentPlace));
if (A <= -8) then {if we are going slow then draw the flame}
begin
CoordAndSize(AllocatedRect, CurrentPlace.left, CurrentPlace.bottom, VFlameRect.right, VFlameRect.bottom);
PlotCIcon(AllocatedRect, CIcons[350 + E]);
if (E = 2) then
E := 0;
E := E + 1;
end;
LastPlace := CurrentPlace;
LastPlace.Bottom := LastPlace.Bottom + VFlameRect.bottom;
end;
end; {draw the missle and its flame (where nessecary) }
procedure DrawPaddle; {this one is kind of complicated, eh?}
var
PaddleRect: Rect;
begin
with WhatOb^ do
begin
PaddleRect := CurrentPlace;
PaddleRect.right := PaddleRect.right + 40;
PaddleRect.left := PaddleRect.left - 40; {create a rect large enough to accomodate the flames}
if E <> 0 then {this is if the smacking trampoline is out}
begin
if E > 0 then
PlotCIcon(ExtraRectA, CIcons[650]);
PaddleRect.top := PaddleRect.top - 20;
E := E - 1; {plot it, enlarge copy/erase rect for it, and make its counter go down}
end;
PlotCIcon(CurrentPlace, CIcons[frame]);
{PaddleRect := BiggestRect(CurrentPlace, LastPlace);}
B := B + 1;
if B = 3 then
B := 0;
case A of {just draw the flame in the right direction}
FLAMERIGHT:
begin
CoordAndSize(AllocatedRect, CurrentPlace.right, CurrentPlace.top + 2, FlameRect.right, FlameRect.bottom);
PlotCIcon(AllocatedRect, CIcons[700 + B]);
end;
FLAMELEFT:
begin
CoordAndSize(AllocatedRect, CurrentPlace.left - FlameRect.right, CurrentPlace.top + 2, FlameRect.right, FlameRect.bottom);
PlotCIcon(AllocatedRect, CIcons[750 + B]);
end;
otherwise
;
end;
LastPlace := PaddleRect;
end;
AddCopyRect(PaddleRect);
{Draw the paddle depednding on where the object is}
end;
procedure DrawPaddlePiece (var WhatOb: ListGameObject);
begin
with WhatOb^ do
begin
if CurrentPlace.top > BOTTOMBOUNDRY then
begin
DeathCountdown := DeathCountdown + 1;
DoDispose := TRUE; {we went off the screen, let's go away}
end
else
PlotCIcon(CurrentPlace, CIcons[frame + C]);
AddCopyRect(BiggestRect(CurrentPlace, LastPlace));
LastPlace := CurrentPlace;
end;
end; {this simply draws a piece of the blown up paddle}
procedure DrawPaddleExp (var WhatOb: ListGameObject);
begin
with WhatOb^ do
begin
if frame = 912 then
begin
DoDispose := TRUE;
end
else
begin
frame := frame + 1;
PlotCIcon(CurrentPlace, CIcons[frame]);
end;
AddCopyRect(LastPlace);
end;
end; {draw the paddle's explosion (not the pieces)}
procedure DrawDeadBrick;
begin
with WhatOb^ do
begin
PlotCIcon(CurrentPlace, CIcons[frame]);
if frame <> 1114 then
frame := frame + 1
else
begin
EraseRect(LastPlace);
DoDispose := TRUE;
end;
AddCopyRect(LastPlace);
end;
end; {this is not used, because my machine was too slow to handle it, but you can put it back in,}
{in the grid collision section}
procedure KABLAMMM;
begin
with WhatOb^ do
begin
if frame = 412 then
begin
DoDispose := TRUE;
end
else
begin
frame := frame + 1;
PlotCIcon(CurrentPlace, CIcons[frame]);
OffsetRect(CurrentPlace, 20, 0);
PlotCIcon(CurrentPlace, CIcons[frame]);
OffsetRect(CurrentPlace, -10, 10);
PlotCIcon(CurrentPlace, CIcons[frame]);
OffsetRect(CurrentPlace, -10, -10);
end;
AddCopyRect(LastPlace);
end;
end; {create the tri-iconed flame. mike wanted the cute function name}
procedure Draw;
begin
if (WhatOb^.ID <> 0) then
begin
case WhatOb^.ID of
PADDLE:
begin
DrawPaddle(WhatOb);
end;
MISSILE:
begin
{UpdateNonMovingGrid(WhatOb);}
DrawMissile(WhatOb);
{Warning: this may be a must-have later}
end;
BALL:
begin
{UpdateNonMovingGrid(WhatOb);}
DrawBall(WhatOb);
end;
EXPLOSION:
begin
KABLAMMM(WhatOb);
end;
BRICK:
begin
DrawBrick(WhatOb);
end;
DEADBRICK:
begin
DrawDeadBrick(WhatOb);
end;
PADDLEPIECE:
DrawPaddlePiece(WhatOb);
PADDLEEXP:
DrawPaddleExp(WhatOb);
end;
if WhatOb^.DoDispose = TRUE then
begin
EraseRect(WhatOb^.LastPlace);
AddCopyRect(WhatOb^.LastPlace); {if the object needs to go away just erase it}
end;
end;
end; {this picks what function to use depending on the object's type. yes, this}
{would be better with function pointers}
procedure UpdateStatusBar;
var
ErasingRect: Rect;
CurrStr: Str255;
begin
BackPixPat(StatusPat);
CoordAndSize(ErasingRect, StatusStart.h, StatusStart.v, RIGHTBOUNDRY, 30);
MoveTo(StatusStart.h, StatusStart.v + 2);
PenSize(3, 3);
Line(RIGHTBOUNDRY, 0);
PenSize(1, 1);
EraseRect(ErasingRect);
ForeColor(BlackColor);
MoveTo(StatusStart.h, StatusStart.v + 16);
TextSize(16);
CurrStr := StringOf('Score: ', score : 10);
DrawString(CurrStr);
MoveTo(StatusStart.h + 175, StatusStart.v + 16);
CurrStr := StringOf('Missiles: ', missiles : 10);
DrawString(CurrStr);
MoveTo(StatusStart.h + 375, StatusStart.v + 16);
CurrStr := StringOf('Lives: ', lives : 10);
DrawString(CurrStr);
TextSize(10);
MoveTo(5, 326);
CurrStr := StringOf('Top Score: ', top : 10);
DrawString(CurrStr);
BackPixPat(BackPat);
AddCopyRect(ErasingRect);
end; {ok, this may be a bit of code, but it's really simple; it just draws all yer info}
procedure DrawStatusBar;
var
ErasingRect: Rect;
begin
BackPixPat(StatusPat);
CoordAndSize(ErasingRect, StatusStart.h, StatusStart.v, RIGHTBOUNDRY, 30);
EraseRect(ErasingRect);
MoveTo(StatusStart.h, StatusStart.v);
PenSize(3, 3);
Line(RIGHTBOUNDRY, 0);
PenSize(1, 1);
UpdateStatusBar;
end; {this erases the place where the status bar goes, i think i remember this is silly for some reason,}
{you can probably figure it out}
procedure FlushLevelGraphics;
begin
CurrentNonMoving := 0;
SetGWorld(GWorldPtr(OffScreenWorld), nil);
DisposePixPat(BackPat);
BackPat := GetPixPat(WhatPixPat);
BackPixPat(BackPat);
EraseRect(Window^.PortRect);
NumberRects := 0;
DrawStatusBar;
SetGWorld(GWorldPtr(Window), GetMainDevice); {go to the window}
{ XCopyBits(Window^.PortRect); (don't ask)}
{CopyBits(OffScreenWorld^.PortBits, Window^.PortBits, Window^.PortRect, Window^.PortRect, srcCopy, nil);}
NextDrawing := TickCount + 5; {this is so the fps remains constant}
SetGWorld(GWorldPtr(OffScreenWorld), nil); {go back to the offscreen world}
end; {Just clear the screen for the next level , right ? }
procedure XCopyBits (Where: Rect);
var
XAmt, YAmt: integer;
ByteG, ByteS, GRow, SRow: Ptr;
begin
ByteG := GWorldStart;
ByteS := ScreenStart;
GRow := ByteG;
SRow := ByteS;
for YAmt := 0 to 470 do
begin
for XAmt := 0 to 630 do
begin
ByteS^ := ByteG^;
ByteG := Ptr(Ord4(ByteG) + 1);
ByteS := Ptr(Ord4(ByteS) + 1);
end;
GRow := Ptr(Ord4(GRow) + GOffset);
SRow := Ptr(Ord4(SRow) + SOffset);
ByteS := SRow;
ByteG := GRow;
{ ByteG := Ptr(Ord4(ByteG) + Offset - 200);}
{ ByteS := Ptr(Ord4(ByteS) + Offset - 200);}
end;
while not button do
;
end; {this is my retarded attempt at a blitter... at least i tried, right?}
procedure DrawCopyRects;
var
i: integer;
CurrRect: Rect;
begin
UpdateStatusBar;
SetGWorld(GWorldPtr(Window), GetMainDevice);
if NumberRects > 3 then
begin
end;
{Wait for next tick}
while TickCount < NextDrawing do
;
if NumberRects < 10 then
begin
for i := 1 to NumberRects do
begin
CurrRect := CopyRects[i];
CopyBits(OffScreenWorld^.PortBits, Window^.PortBits, CurrRect, CurrRect, srcCopy, nil);
end;
end
else
CopyBits(OffScreenWorld^.PortBits, Window^.PortBits, Window^.PortRect, Window^.PortRect, srcCopy, nil);
NextDrawing := TickCount + 3; {fps}
NumberRects := 0;
SetGWorld(GWorldPtr(OffScreenWorld), nil);
end; {draw each rect that needs to be copied (is dirty, they say), and if there's too many just do the whole screen}
procedure RestoreGraphics;
begin
SetGWorld(GWorldPtr(Window), GetMainDevice);
end; {this _could_ do more, but right now it just does the obvious, set the screen back to the window}
procedure FixErasure;
var
CurrentObject: ListGameObject;
begin
CurrentObject := FirstObject;
while (CurrentObject <> nil) do
begin
if CurrentObject^.ID <> 0 then
EraseRect(CurrentObject^.LastPlace);
CurrentObject := CurrentObject^.Next;
end;
end; { Erase all sprites so we can draw em all over again ( whee )}
procedure DrawGridSingleSquare;
var
k: integer;
UpdatePlace: Rect;
begin
for k := 1 to 5 do
if NonMovingGrid[Where.h, Where.v, k] <> nil then
Draw(NonMovingGrid[Where.h, Where.v, k]);
end; {if there's stuff in any of the grid objects, then draw it, eh}
procedure DrawGridSquares;
begin
if (CurrentNonMoving = 0) then
Exit(DrawGridSquares);
while CurrentNonMoving <> 0 do
begin
DrawGridSingleSquare(NonMovingList[CurrentNonMoving]);
CurrentNonMoving := CurrentNonMoving - 1;
end;
CurrentNonMoving := 0;
end; {call each square to draw, only draw what we need to with the currentnonmoving counter}
procedure InitGraphics;
var
i: integer;
begin
randSeed := TickCount;
Window := GetNewCWindow(128, nil, WindowPtr(-1));
SetPort(Window);
if noErr <> NewGWorld(GWorldPtr(OffScreenWorld), 0, Window^.PortRect, nil, nil, [pixelsLocked]) then
; {Hope not to crash}
if LockPixels(CGrafPtr(OffScreenWorld)^.PortPixMap) then
; {Hope not to crash}
GWorldStart := GetPixBaseAddr(GWorldPtr(OffScreenWorld)^.PortPixMap);
ScreenStart := GetPixBaseAddr(GWorldPtr(Window)^.PortPixMap);
TempOff := GWorldPtr(Window);
SOffset := BitAnd(GetGWorldPixMap(TempOff)^^.rowBytes, $3FFF);
TempOff := GWorldPtr(OffScreenWorld);
GOffset := BitAnd(GetGWorldPixMap(TempOff)^^.rowBytes, $3FFF);
SetGWorld(GWorldPtr(OffScreenWorld), nil);
for i := MINCICON to MAXCICON do
CIcons[i] := GetCIcon(i);
FlameRect := CIcons[700]^^.iconpmap.bounds;
VFlameRect := CIcons[350]^^.iconpmap.bounds;
SetRect(GridRect, 0, 0, BLOCKSIZE, BLOCKSIZE);
StatusPat := GetPixPat(130);
StatusStart.h := 0;
StatusStart.v := 300;
end; {this silly function does some really simple init stuff, plus it loads each cicon}
end.